
#include "linkage.h"

// 寄存器/或变量在栈中的位置
R15     =       0x00
R14     =       0x08
R13     =       0x10
R12     =       0x18
R11     =       0x20
R10     =       0x28
R9      =       0x30
R8      =       0x38
RBX     =       0x40
RCX     =       0x48
RDX	    =	    0x50
RSI	    =	    0x58
RDI	    =	    0x60
RBP	    =	    0x68
DS	    =	    0x70
ES	    =	    0x78
RAX	    =	    0x80
FUNC	=	    0x88
ERRCODE	=	    0x90
RIP	    =	    0x98
CS	    =	    0xa0
RFLAGS	=	    0xa8
OLDRSP	=	    0xb0
OLDSS	=	    0xb8

RESTORE_ALL:
    popq %r15;
    popq %r14;
    popq %r13;
    popq %r12;
    popq %r11;
    popq %r10;		
	popq %r9;		
	popq %r8;		
	popq %rbx;		
	popq %rcx;		
	popq %rdx;		
	popq %rsi;		
	popq %rdi;		
	popq %rbp;		
	popq %rax;		
	movq %rax, %ds;	
	popq %rax;		
	movq %rax, %es;	
	popq %rax;		
	addq $0x10, %rsp;	
	iretq;

#define GET_CURRENT(reg)    \
    movq $-32768, reg;  \
    andq %rsp, reg

ret_from_exception:

ENTRY(ret_from_intr)
    jmp RESTORE_ALL     /* need rewrite */

ENTRY(system_call)
	sti
	subq $0x38, %rsp
	cld
	pushq %rax
	movq %es, %rax
	pushq %rax
	movq %ds, %rax
	pushq %rax
	xorq %rax, %rax
	pushq %rbp
	pushq %rdi
	pushq %rsi
	pushq %rdx
	pushq %rcx
	pushq %rbx
	pushq %r8
	pushq %r9
	pushq %r10
	pushq %r11
	pushq %r12
	pushq %r13
	pushq %r14
	pushq %r15
	movq $0x10, %rdx
	movq %rdx, %ds
	movq %rdx, %es
	movq %rsp, %rdi
	callq system_call_function

ENTRY(ret_system_call)
	movq %rax, 0x80(%rsp)
	popq %r15
	popq %r14
	popq %r13
    popq %r12
    popq %r11
    popq %r10		
	popq %r9		
	popq %r8		
	popq %rbx	
	popq %rcx	
	popq %rdx	
	popq %rsi	
	popq %rdi	
	popq %rbp	
	popq %rax	
	movq %rax, %ds
	popq %rax	
	movq %rax, %es
	popq %rax	
	addq $0x38, %rsp;
	.byte 0x48								// sysexit在64位模式下默认操作数不是64位，要返回64位模式，必须加0x48前缀修饰
	sysexit

ENTRY(divide_error)
    pushq $0                                // ERRCODE入栈，divide没有错误代码，填0
    pushq %rax                              // eax入栈，后面会改变这个值
    leaq do_divide_error(%rip), %rax        // 获取do_divide_error函数地址存入rax
    xchgq %rax, (%rsp)                      // 交换rax和栈顶的值，相当于FUNC入栈

error_code:
    pushq %rax                              // rax入栈
	movq %es, %rax                          // rax = es
	pushq %rax                              // es入栈
	movq %ds, %rax                          // rax = ds
	pushq %rax                              // ds入栈
	xorq %rax, %rax                         // rax = 0

	pushq %rbp                              // rbp入栈，后续按照定义的顺序入栈
	pushq %rdi
	pushq %rsi
	pushq %rdx
	pushq %rcx
	pushq %rbx
	pushq %r8
	pushq %r9
	pushq %r10
	pushq %r11
	pushq %r12
	pushq %r13
	pushq %r14
	pushq %r15	
	
	cld                                     // 清空DF标志
	movq ERRCODE(%rsp),	%rsi                // rsi = 错误代码
	movq FUNC(%rsp),	%rdx	            // rdx = 函数地址

	movq $0x10,	%rdi                        // rdi = 0x10
	movq %rdi, %ds                          // ds = 0x10
	movq %rdi, %es                          // es = 0x10

	movq %rsp, %rdi                         // rdi = rsp
	////GET_CURRENT(%ebx)

	callq *%rdx                             // 调用函数，*表示绝对地址，默认不带*表示相对地址，函数传参采用寄存器，从左到右依次使用RDI,RSI,RDX,RCX,R8,R9

	jmp	ret_from_exception                  // 跳转到ret_from_exception，最后恢复现场

ENTRY(debug)
    pushq $0
    pushq %rax
    leaq do_debug(%rip), %rax
    xchgq %rax, (%rsp)
    jmp error_code

ENTRY(nmi)
    pushq %rax
	cld;			
	pushq %rax;	
	
	pushq %rax
	movq %es, %rax
	pushq %rax
	movq %ds, %rax
	pushq %rax
	xorq %rax, %rax
	
	pushq %rbp;		
	pushq %rdi;		
	pushq %rsi;		
	pushq %rdx;		
	pushq %rcx;		
	pushq %rbx;		
	pushq %r8;		
	pushq %r9;		
	pushq %r10;		
	pushq %r11;		
	pushq %r12;		
	pushq %r13;		
	pushq %r14;		
	pushq %r15;
	
	movq $0x10, %rdx;	
	movq %rdx, %ds;	
	movq %rdx, %es;
	
	movq $0, %rsi
	movq %rsp, %rdi

	callq do_nmi

	jmp	RESTORE_ALL

ENTRY(int3)
	pushq $0
	pushq %rax
	leaq do_int3(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(overflow)
	pushq $0
	pushq %rax
	leaq do_overflow(%rip),	%rax
	xchgq %rax, (%rsp)
	jmp	error_code

ENTRY(bounds)
	pushq $0
	pushq %rax
	leaq do_bounds(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(undefined_opcode)
	pushq $0
	pushq %rax
	leaq do_undefined_opcode(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(dev_not_available)	/* need rewrite */
	pushq $0
	pushq %rax
	leaq do_dev_not_available(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code


ENTRY(double_fault)
	pushq %rax
	leaq do_double_fault(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(coprocessor_segment_overrun)
	pushq $0
	pushq %rax
	leaq do_coprocessor_segment_overrun(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(invalid_TSS)
	pushq %rax
	leaq do_invalid_TSS(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(segment_not_present)
	pushq %rax
	leaq do_segment_not_present(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(stack_segment_fault)
	pushq %rax
	leaq do_stack_segment_fault(%rip),	%rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(general_protection)
	pushq %rax
	leaq do_general_protection(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(page_fault)
	pushq %rax
	leaq do_page_fault(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(x87_FPU_error)
	pushq $0
	pushq %rax
	leaq do_x87_FPU_error(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(alignment_check)
	pushq %rax
	leaq do_alignment_check(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(machine_check)
	pushq $0
	pushq %rax
	leaq do_machine_check(%rip), %rax
	xchgq %rax,	(%rsp)
	jmp	error_code

ENTRY(SIMD_exception)
	pushq $0
	pushq %rax
	leaq do_SIMD_exception(%rip), %rax
	xchgq %rax, (%rsp)
	jmp	error_code

ENTRY(virtualization_exception)
	pushq $0
	pushq %rax
	leaq do_virtualization_exception(%rip), %rax
	xchgq %rax, (%rsp)
	jmp	error_code